grammar Yagdao;

options {
  language = Java;
  output = AST;

}
tokens {
  NEGATION;
}


@members {
    private List<String> errors = new LinkedList<String>();
    public void displayRecognitionError(String[] tokenNames,
                                        RecognitionException e) {
        String hdr = getErrorHeader(e);
        String msg = getErrorMessage(e, tokenNames);
        errors.add(hdr + " " + msg);
    }
    public List<String> getErrors() {
        return errors;
    }
}

@header {
  package com.altuure.yagdao.grammar;
  import java.util.List;
  import java.util.LinkedList;
}

@lexer::header {
  package com.altuure.yagdao.grammar;
  import java.util.List;
  import java.util.LinkedList;
}



identifier : (ID DOT)* ID ;



result_list :(DISTINCT)? result_col (COMMA result_col)* EOF!;
result_col: expression;

condition_col: HAVING? expression (condition expression?)? EOF!;
//backward compatible will be HAVING? expression condition expression?

group_col: expression (COMMA expression)* EOF!;

where_condition:  
     (LPAREN condition_seq RPAREN) =>inner_col
     |expression condition expression?
    
;
where_clause:
    condition_seq EOF!;

condition_seq: 
  where_condition (where_op where_condition)*;
       
having_col: where_condition (where_op where_condition)*;   

inner_col : LPAREN! condition_seq RPAREN!;

order_property:  expression order_op?;
order_clause:  order_property (COMMA order_property)* EOF!;

where_op
  : AND
    |OR
  ;
  
order_op
  : ASC|DESC
  ;


condition:    GREATER
              |LESS
              |EQUALS
              |EQUALS2
              |NOT_EQUALS
              |NOT_EQUALS2
              |LESS_OR_EQ
              |GREATER_OR_EQ
              |IN
              |NOT_IN
              |LIKE
              |ILIKE
              |ISNULL
              |ISNOTNULL
              |BETWEEN
              ;



term
  : identifier
  | LPAREN! expression RPAREN!
  | invocation 
  | QUESTION
  | INTEGER
  | FLOAT
  | STRING_LITERAL
  ;
  
unary
  : (PLUS! | negation^)* term
  ;

negation
  : MINUS -> NEGATION
  ;

mult
  : unary ((ASTERISK^ | SLASH^ | MOD^) unary)*
  ;
  
  
expression
  : mult ((PLUS^ | MINUS^) mult)*
  ;
invocation:  functionname LPAREN  argumentlist? RPAREN;
argumentlist: expression (COMMA expression)* ;

functionname
  : 
  AVG|MIN |MAX |SUM|COUNT
  |NEG|ABS|SQRT|TOFLOAT|TOINTEGER|TOLONG|TOBIGDECIMAL|TOBIGINTEGER|TOSTRING
  |CONCAT|SUBSTRING|TRIM|LOWER|UPPER|LENGTH|LOCATE
  |CURRENTDATE|CURRENTTIME|CURRENTTIMESTAMP
  |SECOND|MINUTE|HOUR|DAY|MONTH|YEAR
  |MOD
  |unknownFunctionName; 
unknownFunctionName: ID;  

//
// Lexer
//

EQUALS:        '=';
EQUALS2:       '==';
NOT_EQUALS:    '!=';
NOT_EQUALS2:   '<>';
LESS:          '<';
LESS_OR_EQ:    '<=';
GREATER:       '>';
GREATER_OR_EQ: '>=';
SHIFT_LEFT:    '<<';
SHIFT_RIGHT:   '>>';
AMPERSAND:     '&';
PIPE:          '|';
DOUBLE_PIPE:   '||';
PLUS:          '+';
MINUS:         '-';
TILDA:         '~';
ASTERISK:      '*';
SLASH:         '/';
BACKSLASH:     '\\';
PERCENT:       '%';
SEMI:          ';';
DOT:           '.';
COMMA:         ',';
LPAREN:        '(';
RPAREN:        ')';
QUESTION:      '?';
COLON:         ':';
AT:            '@';
DOLLAR:        '$';
QUOTE_DOUBLE:  '"';
QUOTE_SINGLE:  '\'';
APOSTROPHE:    '`';
LPAREN_SQUARE: '[';
RPAREN_SQUARE: ']';
UNDERSCORE:    '_';


// http://www.antlr.org/wiki/pages/viewpage.action?pageId=1782
fragment A:('a'|'A');
fragment B:('b'|'B');
fragment C:('c'|'C');
fragment D:('d'|'D');
fragment E:('e'|'E');
fragment F:('f'|'F');
fragment G:('g'|'G');
fragment H:('h'|'H');
fragment I:('i'|'I');
fragment J:('j'|'J');
fragment K:('k'|'K');
fragment L:('l'|'L');
fragment M:('m'|'M');
fragment N:('n'|'N');
fragment O:('o'|'O');
fragment P:('p'|'P');
fragment Q:('q'|'Q');
fragment R:('r'|'R');
fragment S:('s'|'S');
fragment T:('t'|'T');
fragment U:('u'|'U');
fragment V:('v'|'V');
fragment W:('w'|'W');
fragment X:('x'|'X');
fragment Y:('y'|'Y');
fragment Z:('z'|'Z');



SUM: S U M;
AVG: A V G;
MIN: M I N;
MAX : M A X;
COUNT:C O U N T;
NEG:N E G;
ABS:A B S;
SQRT:S Q R T;
TOFLOAT:T O F L O A T;
TOINTEGER:T O I N T E G E R;
TOLONG:T O L O N G;
TOBIGDECIMAL:T O B I G D E C I M A L;
TOBIGINTEGER:T O B I G I N T E G E R;
TOSTRING:T O S T R I N G;
CONCAT:C O N C A T;
SUBSTRING:S U B S T R I N G;
TRIM:T R I M;
LOWER:L O W E R;
UPPER:U P P E R;
LENGTH:L E N G T H ;
LOCATE:L O C A T E;
CURRENTDATE:C U R R E N T D A T E;
CURRENTTIMESTAMP:C U R R E N  T T I M E S T A M P;
CURRENTTIME:C U R R E N T T I M E;
MOD : M O D;
SECOND:S E C O N D;
MINUTE:M I N U T E;
HOUR:H O U R;
DAY:D A Y;
MONTH:M O N T H;
YEAR:Y E A R;

HAVING: H A V I N G;
ASC: A S C;
DESC: D E S C;
AND: A N D;
OR : O R;

ISNULL : IS WS NULL ;
ISNOTNULL : IS WS NOT WS NULL;
NOT_IN :NOT WS IN;


DISTINCT : D I S T I N C T;
LIKE: L I K E;
ILIKE: I L I K E;
IN: I N;
IS: I S;
NOT : N O T;
NULL: N U L L;
BETWEEN: B E T W E E N;


ID: ID_PLAIN;
fragment ID_START: ('a'..'z'|'A'..'Z'|UNDERSCORE);
fragment ID_CORE: (ID_START|'0'..'9'|DOLLAR);
fragment ID_PLAIN: ID_START (ID_CORE)*;

STRING_LITERAL
  : '"'
    { StringBuilder b = new StringBuilder(); }
    ( '"' '"'       { b.appendCodePoint('"');}
    | c=~('"'|'\r'|'\n')  { b.appendCodePoint(c);}
    )*
    '"'
    { setText(b.toString()); }
  ;
  
CHAR_LITERAL
  : '\'' . '\'' {setText(getText().substring(1,2));}
  ;
INTEGER: ('0'..'9')+;
fragment FLOAT_EXP : ('e'|'E') ('+'|'-')? ('0'..'9')+ ;
FLOAT
    :   ('0'..'9')+ DOT ('0'..'9')* FLOAT_EXP?
    |   DOT ('0'..'9')+ FLOAT_EXP?
    |   ('0'..'9')+ FLOAT_EXP
    ;
BLOB: ('x'|'X') QUOTE_SINGLE ('0'..'9'|'a'..'f'|'A'..'F')+ QUOTE_SINGLE;
WS: (' '|'\r'|'\t'|'\u000C'|'\n') {$channel=HIDDEN;};